iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0

狀態模式使用狀態物件來表示物件在不同狀態下的行為,並通過改變狀態物件來改變行為。

生活範例

街道中的紅綠燈非常適合用來說明狀態模式的概念。紅綠燈有三種狀態:紅燈、黃燈和綠燈,分別代表禁止通行、減速慢行和自由通行。這些狀態擁有不同的呈現方式與提示作用,儘管紅綠燈不會隨著狀態的變化而改變自身行為,用路人卻會隨著不同的狀態而改變行為,這與狀態模式中一種狀態對應一種行為的精神十分相似。

舉個例子

在前端的世界裡,狀態模式無所不在。我們透過改變狀態來改變元件的外觀、行為,甚至是事件的反應,可以說狀態是我們最好的朋友也不為過。不過,狀態模式並不屬於前端的專利,任何帶有狀態的物件都能藉由它來管理行為。讓我們看看如何使用狀態模式來管理音樂播放器的播放與暫停狀態。

PlayerState 是播放器狀態的介面,包含一個 handle 方法,讓具體狀態來定義如何處理播放器的狀態轉換。

interface PlayerState {
  handle(player: MusicPlayer): void;
}

定義具體的播放器狀態。播放器會執行當前狀態的 handle 方法,將狀態切換成播放或停止。

class PlayingState implements PlayerState {
  handle(player: MusicPlayer) {
    player.setState(new PausedState());
    console.log("Music is now Paused.");
  }
}

class PausedState implements PlayerState {
  handle(player: MusicPlayer) {
    player.setState(new PlayingState());
    console.log("Music is now Playing.");
  }
}

MusicPlayer 是音樂播放器,currentState 屬性代表播放器的播放狀態。

class MusicPlayer {
  private currentState: PlayerState;

  constructor() {
    this.currentState = new PausedState();
  }

  setState(state: PlayerState) {
    this.currentState = state;
  }

  pressPlayPause() {
    this.currentState.handle(this);
  }
}

建立一個音樂播放器,然後重複呼叫 pressPlayPause() 來觀察播放器狀態在播放與暫停之間的切換。

class MusicPlayerTestDrive {
  static main() {
    const player = new MusicPlayer();

    player.pressPlayPause();
    player.pressPlayPause();
    player.pressPlayPause();
  }
}

MusicPlayerTestDrive.main();

定義

State Pattern

  • 環境(Context): 在不同狀態下展現不同行為的物件
  • 抽象狀態(State): 定義物件具備哪些行為
  • 具體狀態(ConcreteState): 提供行為的具體實現

狀態模式透過狀態物件來管理物件在不同狀態下的行為。每個狀態物件代表一種特定的狀態與其對應的行為模式。當物件的狀態改變時,只需更換對應的狀態物件,物件的行為就會隨之改變。我們不需要透過條件判斷式來改變程式的行為,這讓程式變得更容易閱讀。同時,我們可以透過新增或修改狀態物件來改變物件的行為,無需修改原有的程式邏輯,使得程式更容易擴充與維護。

總結

  • 將不同狀態下的行為封裝成不同的狀態物件
  • 不使用條件判斷式,而是透過替換狀態物件來改變行為
  • 通過新增或調整狀態物件來改變物件的行為,而無需修改既有程式
  • 提升程式的可讀性,並讓程式更容易擴充與維護

完整範例

https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/behavioral/state.ts


上一篇
Day 24 - Bridge 橋接
下一篇
Day 26 - Memento 備忘錄
系列文
前端也想學設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言